home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / Digsby build 37 / digsby_setup.exe / lib / gui / uberwidgets / umenu.pyo (.txt) < prev   
Python Compiled Bytecode  |  2008-10-13  |  43KB  |  1,343 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. from __future__ import with_statement
  5. from gui.skin.skinobjects import SkinColor
  6. import wx
  7. from wx import RectPS, Rect, ITEM_NORMAL, ITEM_SEPARATOR, ITEM_CHECK, ITEM_RADIO, Point, ALIGN_CENTER_VERTICAL, FindWindowAtPoint, MenuItem, CallLater, FindWindowAtPointer, GetMousePosition, wxEVT_MOTION, StockCursor, CURSOR_DEFAULT, GetMouseState, Window
  8. from wx import PyCommandEvent, wxEVT_MENU_OPEN
  9. from gui import skin
  10. from traceback import print_exc
  11. from gui.textutil import default_font
  12. from gui.windowfx import fadein
  13. from gui.vlist.skinvlist import SkinVListBox
  14. from gui.uberwidgets.UberButton import UberButton
  15. from gui.skin.skinobjects import Margins
  16. from gui.uberwidgets.skinnedpanel import SkinnedPanel
  17. from gui.uberwidgets.keycatcher import KeyCatcher
  18. from cgui import SplitImage4
  19. from gui.toolbox import Monitor
  20. from util import traceguard, memoize, Delegate, Storage as S, InstanceTracker
  21. from common import prefprop
  22. from weakref import ref
  23. from logging import getLogger
  24. log = getLogger('umenu')
  25. wxMSW = 'wxMSW' in wx.PlatformInfo
  26. WM_INITMENUPOPUP = 279
  27.  
  28. def MenuItem_repr(item):
  29.     text = None if item.IsSeparator() else item.Label
  30.     return '<%s %s>' % (item.__class__.__name__, text)
  31.  
  32. MenuItem.__repr__ = MenuItem_repr
  33. del MenuItem_repr
  34.  
  35. MenuItem.SetCallback = lambda item, callback: item.Menu.SetCallback(item.Id, callback)
  36.  
  37. class UMenuTrayTimer(wx.Timer):
  38.     
  39.     def __init__(self, umenu):
  40.         wx.Timer.__init__(self)
  41.         self.umenu = umenu
  42.  
  43.     
  44.     def Notify(self):
  45.         
  46.         try:
  47.             mp = GetMousePosition()
  48.             ms = GetMouseState()
  49.         except Exception:
  50.             return None
  51.  
  52.         if not ms.LeftDown() and ms.RightDown() or ms.MiddleDown():
  53.             return None
  54.         
  55.         menu = self.umenu
  56.         while menu != None:
  57.             if menu.ScreenRect.Contains(mp):
  58.                 return None
  59.                 continue
  60.             submenu = menu.menu._childmenu
  61.             menu = None if submenu is not None else None
  62.         self.Stop()
  63.         self.umenu.Dismiss()
  64.  
  65.  
  66.  
  67. class UMenu(wx.Menu, InstanceTracker):
  68.     last_menu = None
  69.     
  70.     def __init__(self, parent, label = '', id = None, onshow = None, windowless = None):
  71.         if not isinstance(parent, wx.WindowClass):
  72.             raise TypeError('UMenu parent must be a wx.Window')
  73.         
  74.         wx.Menu.__init__(self, label)
  75.         InstanceTracker.track(self)
  76.         if not isinstance(id, (int, type(None))):
  77.             raise TypeError
  78.         
  79.         self._parentmenu = None
  80.         self._childmenu = None
  81.         self.Id = None if id is None else id
  82.         self.Window = parent
  83.         self.OnDismiss = Delegate()
  84.         self.cbs = { }
  85.         if onshow is not None:
  86.             self.Handler.AddShowCallback(self.Id, (lambda menu = (ref(self),): onshow(menu())))
  87.         
  88.         if wxMSW:
  89.             self.Handler.hwndMap[self.HMenu] = self
  90.         
  91.         self._windowless = windowless
  92.         self.UpdateSkin()
  93.  
  94.     
  95.     def SetWindowless(self, val):
  96.         self._windowless = val
  97.  
  98.     Windowless = property((lambda self: self._windowless), SetWindowless)
  99.     
  100.     def IsShown(self):
  101.         if not self.popup:
  102.             return False
  103.         
  104.         
  105.         try:
  106.             return self.popup.IsShown()
  107.         except AttributeError:
  108.             return False
  109.  
  110.  
  111.     
  112.     def UpdateSkin(self):
  113.         mbskin = skin.get('MenuBar', None)
  114.         if 'wxMac' in wx.PlatformInfo and not mbskin and mbskin.get('menuskin', None) is None or mbskin.get('mode', 'skin').lower() == 'native':
  115.             self.skin = S(native = True)
  116.             native = True
  117.         else:
  118.             self.skin = skin.get(mbskin.menuskin)
  119.             native = False
  120.             self.skin.native = False
  121.         if not native and not hasattr(self, 'popup'):
  122.             self.popup = MenuPopupWindow(self.Window, self)
  123.         elif not native:
  124.             self.popup.UpdateSkin()
  125.             self.popup.vlist.UpdateSkin()
  126.         elif native:
  127.             if hasattr(self, 'popup'):
  128.                 self.popup.Destroy()
  129.                 del self.popup
  130.             
  131.         
  132.  
  133.     
  134.     def Display(self, caller = None):
  135.         self.PopupMenu(caller.ScreenRect)
  136.  
  137.     
  138.     def dismiss_old(self):
  139.         menuref = UMenu.last_menu
  140.         if menuref is None:
  141.             return None
  142.         
  143.         menu = menuref()
  144.         if menu is not None and not wx.IsDestroyed(menu):
  145.             menu.Dismiss()
  146.         
  147.         UMenu.last_menu = None
  148.  
  149.     
  150.     def PopupMenu(self, pos = None, submenu = False, event = None):
  151.         if not submenu:
  152.             self.dismiss_old()
  153.         
  154.         if event is not None:
  155.             event.Skip(False)
  156.         
  157.         if 'wxMSW' in wx.PlatformInfo and self._windowless:
  158.             _smokeFrame = _smokeFrame
  159.             import gui.native.win.wineffects
  160.             if _smokeFrame:
  161.                 _smokeFrame.SetFocus()
  162.             
  163.         
  164.         
  165.         try:
  166.             onshow = self._onshow
  167.         except AttributeError:
  168.             pass
  169.  
  170.         onshow(self)
  171.         traceguard.__enter__()
  172.         
  173.         try:
  174.             return popup(pos)
  175.         finally:
  176.             pass
  177.  
  178.  
  179.     
  180.     def Dismiss(self):
  181.         if not self.skin.get('native', False) and not wx.IsDestroyed(self.popup):
  182.             return self.popup.vlist.Dismiss()
  183.         
  184.  
  185.     if wxMSW:
  186.         if hasattr(wx.Menu, 'GetHMenu'):
  187.             GetHMenu = wx.Menu.GetHMenu
  188.         else:
  189.             
  190.             def GetHMenu(self):
  191.                 cast = cast
  192.                 POINTER = POINTER
  193.                 c_long = c_long
  194.                 import ctypes
  195.                 p = cast(int(self.this), POINTER(c_long))
  196.                 return p[25]
  197.  
  198.         HMenu = property(GetHMenu)
  199.     
  200.     
  201.     def AddItem(self, text = '', bitmap = None, callback = None, id = -1):
  202.         return self._additem(text, bitmap, callback, id = id)
  203.  
  204.     
  205.     def AddItemAt(self, position, text = '', bitmap = None, callback = None, id = -1):
  206.         return self._additem(text, bitmap, callback, id = id, position = position)
  207.  
  208.     
  209.     def Append(self, id, text, bitmap = None, callback = None):
  210.         return self._additem(text, bitmap, callback, id = id)
  211.  
  212.     
  213.     def AddCheckItem(self, text, callback = None):
  214.         return self._additem(text, callback = callback, kind = ITEM_CHECK)
  215.  
  216.     
  217.     def AddRadioItem(self, text, callback = None):
  218.         return self._additem(text, callback = callback, kind = ITEM_RADIO)
  219.  
  220.     
  221.     def AddPrefCheck(self, pref, text, help = '', updatenow = True):
  222.         profile = profile
  223.         import common
  224.         prefs = profile.prefs
  225.         
  226.         def callback():
  227.             prefs[pref] = not prefs[pref]
  228.  
  229.         item = self._additem(text, callback = callback, kind = ITEM_CHECK)
  230.         prefs.link((pref,), (lambda val: item.Check(val)), obj = self)
  231.         return item
  232.  
  233.     
  234.     def AppendLazyMenu(self, name, callback, bitmap = None):
  235.         if not callable(callback):
  236.             raise TypeError, repr(callback)
  237.         
  238.         menu = UMenu(self.Window)
  239.         return self.AddSubMenu(menu, name, bitmap = bitmap, onshow = (lambda menu = (menu,): callback(menu)))
  240.  
  241.     
  242.     def _additem(self, text, bitmap = None, callback = None, kind = ITEM_NORMAL, id = -1, position = None):
  243.         item = MenuItem(self, id, text, kind = kind)
  244.         id = item.Id
  245.         if bitmap is not None:
  246.             self.SetItemBitmap(item, bitmap)
  247.         
  248.         if callback is not None:
  249.             self.SetCallback(id, callback)
  250.         
  251.         if position is None:
  252.             self.AppendItem(item)
  253.         else:
  254.             self.InsertItem(position, item)
  255.         return item
  256.  
  257.     
  258.     def SetCallback(self, id, callback):
  259.         
  260.         callback = lambda cb = (callback,): self._refresh_callback(cb)
  261.         self.cbs[id] = callback
  262.         self.Handler.AddCallback(id, callback)
  263.  
  264.     
  265.     def _refresh_callback(self, cb):
  266.         m = self._parentmenu
  267.         if m is None:
  268.             m = self
  269.         else:
  270.             while m._parentmenu:
  271.                 m = m._parentmenu
  272.         if hasattr(m, '_button'):
  273.             if wx.IsDestroyed(m._button):
  274.                 del m._button
  275.             else:
  276.                 m._button.Refresh()
  277.                 m._button.Update()
  278.         
  279.         self.Window.Refresh()
  280.         self.Window.Update()
  281.         return cb()
  282.  
  283.     
  284.     def AddSubMenu(self, submenu, label, bitmap = None, onshow = None):
  285.         submenu._parentmenu = self
  286.         if onshow is not None:
  287.             self.Handler.AddShowCallback(submenu.Id, onshow)
  288.         
  289.         item = self.AppendSubMenu(submenu, label)
  290.         if bitmap is not None:
  291.             self.SetItemBitmap(item, bitmap)
  292.         
  293.         return item
  294.  
  295.     
  296.     def SetItemBitmap(self, item, bitmap):
  297.         if self.skin.native and bitmap.Ok():
  298.             bitmap = bitmap.ResizedSmaller(16)
  299.         
  300.         item.SetBitmap(bitmap)
  301.  
  302.     
  303.     def AddSep(self):
  304.         return self.AppendItem(MenuItem(self))
  305.  
  306.     
  307.     def AddSepAt(self, i):
  308.         return self.InsertItem(i, MenuItem(self))
  309.  
  310.     
  311.     def RemoveItems(self, items):
  312.         return [ self.RemoveItem(item) for item in items ]
  313.  
  314.     
  315.     def RemoveAll(self):
  316.         return self.RemoveItems(list(self))
  317.  
  318.     
  319.     def DestroyAll(self):
  320.         for item in self.RemoveAll():
  321.             item.Destroy()
  322.         
  323.  
  324.     
  325.     def GetItemById(self, id):
  326.         for i, myitem in enumerate(self):
  327.             if myitem.Id == id:
  328.                 return myitem
  329.                 continue
  330.         
  331.  
  332.     
  333.     def IndexOf(self, item):
  334.         id = item.Id
  335.         for i, myitem in enumerate(self):
  336.             if myitem.Id == id:
  337.                 return i
  338.                 continue
  339.         
  340.         return -1
  341.  
  342.     
  343.     def Break(self):
  344.         raise NotImplementedError('skinned menus cannot break')
  345.  
  346.     
  347.     def Top(self):
  348.         w = self.Window
  349.         while not isinstance(w, wx.TopLevelWindow):
  350.             
  351.             try:
  352.                 w = getattr(w, 'Window', getattr(w, 'ParentWindow', w.Parent))
  353.             continue
  354.             except AttributeError:
  355.                 print '***', w, '***'
  356.                 raise 
  357.                 continue
  358.             
  359.  
  360.             None<EXCEPTION MATCH>AttributeError
  361.         return w
  362.  
  363.     Top = property(Top)
  364.     
  365.     def Handler(self):
  366.         return menuEventHandler(self.Top)
  367.  
  368.     Handler = property(Handler)
  369.     
  370.     def _activate_item(self, item):
  371.         return self.popup.vlist._activate_item(item)
  372.  
  373.     
  374.     def __iter__(self):
  375.         return iter(self.GetMenuItems())
  376.  
  377.     
  378.     def __getitem__(self, n):
  379.         return self.GetMenuItems()[n % len(self)]
  380.  
  381.     
  382.     def __len__(self):
  383.         return len(self.GetMenuItems())
  384.  
  385.     
  386.     def __contains__(self, item):
  387.         for i in self:
  388.             if i.Id == item.Id:
  389.                 return True
  390.                 continue
  391.         
  392.         return False
  393.  
  394.     
  395.     def __repr__(self):
  396.         return '<%s %r>' % (self.__class__.__name__, self.Title)
  397.  
  398.  
  399.  
  400. class UMenuBar(wx.MenuBar):
  401.     
  402.     def __init__(self, parent, skinkey = 'MenuBar'):
  403.         wx.MenuBar.__init__(self)
  404.         self.toptitles = { }
  405.         self.skinkey = skinkey
  406.         self.accelremoves = Delegate()
  407.         self.panel = SkinnedPanel(parent, 'MenuBar')
  408.         self.panel.UpdateSkin = self.UpdateSkin
  409.         self.panel.Hide()
  410.         self.UpdateSkin()
  411.  
  412.     
  413.     def UpdateSkin(self):
  414.         s = self.skin = skin.get(self.skinkey)
  415.         if not s.get('mode', 'skin').lower() == 'native':
  416.             pass
  417.         self.native = 'wxMac' in wx.PlatformInfo
  418.         if not hasattr(self, 'panel'):
  419.             return None
  420.         
  421.         if self.native:
  422.             for child in self.panel.Children:
  423.                 traceguard.__enter__()
  424.                 
  425.                 try:
  426.                     child.Destroy()
  427.                 finally:
  428.                     pass
  429.  
  430.             
  431.             self.panel.Hide()
  432.             self.ParentWindow.Top.SetMenuBar(self)
  433.         else:
  434.             self._constructSkinElements()
  435.             win = self.ParentWindow.Top
  436.             if win.MenuBar is self:
  437.                 win.SetMenuBar(None)
  438.             
  439.             self.panel.Sizer.Layout()
  440.             self.panel.Show(self.IsShown())
  441.  
  442.     
  443.     def Append(self, menu, title, onshow = None):
  444.         self.toptitles[menu] = title
  445.         i = wx.MenuBar.Append(self, menu, title)
  446.         if not self.native:
  447.             self._constructSkinElements()
  448.         
  449.         if onshow is not None:
  450.             self.Handler.AddShowCallback(menu.Id, (lambda menu = (menu,): onshow(menu)))
  451.         
  452.         return i
  453.  
  454.     
  455.     def _constructSkinElements(self):
  456.         s = self.skin
  457.         p = self.panel
  458.         p.bg = s.get('background', SkinColor(wx.WHITE))
  459.         pad = p.padding = s.get('padding', wx.Point(0, 0))
  460.         for child in list(p.Children):
  461.             child.Hide()
  462.             child.Destroy()
  463.         
  464.         v = wx.BoxSizer(wx.VERTICAL)
  465.         h = wx.BoxSizer(wx.HORIZONTAL)
  466.         v.Add(h, 1, wx.EXPAND | wx.TOP | wx.BOTTOM, pad.y)
  467.         p.Sizer = s.get('margins', Margins()).Sizer(v)
  468.         self.buttons = []
  469.         addb = self.buttons.append
  470.         menus = self.Menus
  471.         nummenus = len(self.Menus)
  472.         for menu, label in enumerate(menus):
  473.             del menu.OnDismiss[:]
  474.             label = self.toptitles.get(menu, label)
  475.             button = UberButton(p, -1, skin = s.itemskin, label = label, type = 'menu', menu = menu, menubarmode = True)
  476.             addb(button)
  477.             menu._next = menus[(i + 1) % nummenus][0]
  478.             menu._prev = menus[i - 1][0]
  479.             menu._button = button
  480.         
  481.         (h.AddMany,)((lambda .0: for b in .0:
  482. (b, 0, wx.EXPAND | wx.LEFT, pad.x))(self.buttons))
  483.         self._bindAccelerators()
  484.  
  485.     
  486.     def Show(self, val):
  487.         native = self.native
  488.         if native:
  489.             win = self.ParentWindow.Top
  490.             self.panel.Hide()
  491.             if val and win.MenuBar is not self:
  492.                 win.MenuBar = self
  493.             elif not val and win.MenuBar is self:
  494.                 win.MenuBar = None
  495.             
  496.         else:
  497.             self.panel.Show(val)
  498.  
  499.     
  500.     def _bindAccelerators(self):
  501.         accelrems = self.accelremoves
  502.         parentproc = self.ParentWindow.ProcessEvent
  503.         keybind = self.KeyCatcher.OnDown
  504.         accelrems()
  505.         del accelrems[:]
  506.         for menu in self:
  507.             for item in menu:
  508.                 accel = GetAccelText(item)
  509.                 if accel:
  510.                     
  511.                     cb = lambda e, item = item, menu = (menu,): parentproc(menuevt(item))
  512.                     accelrems.append(keybind(accel, cb))
  513.                     continue
  514.             
  515.         
  516.  
  517.     
  518.     def KeyCatcher(self):
  519.         
  520.         try:
  521.             return self.ParentWindow._keycatcher
  522.         except AttributeError:
  523.             k = self.ParentWindow._keycatcher = KeyCatcher(self.ParentWindow)
  524.             return k
  525.  
  526.  
  527.     KeyCatcher = property(KeyCatcher)
  528.     
  529.     def __len__(self):
  530.         return self.GetMenuCount()
  531.  
  532.     
  533.     def __iter__(self):
  534.         return []([ self.GetMenu(i) for i in xrange(self.GetMenuCount()) ])
  535.  
  536.     
  537.     def SizableWindow(self):
  538.         return self.panel
  539.  
  540.     SizableWindow = property(SizableWindow)
  541.     
  542.     def ParentWindow(self):
  543.         return self.panel.Parent
  544.  
  545.     ParentWindow = property(ParentWindow)
  546.     
  547.     def Handler(self):
  548.         return menuEventHandler(self.panel.Top)
  549.  
  550.     Handler = property(Handler)
  551.  
  552. registered_activate_app = False
  553.  
  554. def _activateapp(e):
  555.     vlist = MenuListBox._lastvlist()
  556.     if vlist is not None and not wx.IsDestroyed(vlist) and not wx.IsDestroyed(vlist.menu):
  557.         if vlist.menu and vlist.menu._windowless and not e.GetActive():
  558.             vlist.DismissRoot()
  559.         
  560.     
  561.  
  562.  
  563. class MenuListBox(SkinVListBox):
  564.     
  565.     def __init__(self, parent, menu):
  566.         global registered_activate_app
  567.         SkinVListBox.__init__(self, parent, style = wx.NO_BORDER | wx.FULL_REPAINT_ON_RESIZE | wx.WANTS_CHARS)
  568.         self.menu = menu
  569.         self.UpdateSkin()
  570.         self.timer = wx.PyTimer(self._on_submenu_timer)
  571.         Bind = self.Bind
  572.         Bind(wx.EVT_MOUSE_EVENTS, self._mouseevents)
  573.         Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, self._capturechanged)
  574.         Bind(wx.EVT_LISTBOX, self._listbox)
  575.         Bind(wx.EVT_KEY_DOWN, self._keydown)
  576.         self.mouseCallbacks = {
  577.             wx.wxEVT_MOTION: self._motion,
  578.             wx.wxEVT_RIGHT_DOWN: self._rdown,
  579.             wx.wxEVT_LEFT_DOWN: self._ldown,
  580.             wx.wxEVT_LEFT_UP: self._lup }
  581.         MenuListBox._lastvlist = ref(self)
  582.         if not registered_activate_app:
  583.             wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, _activateapp)
  584.             registered_activate_app = True
  585.         
  586.  
  587.     
  588.     def reassign(self, menu):
  589.         self.menu = menu
  590.  
  591.     menuOpenDelayMs = prefprop('menus.submenu_delay', 250)
  592.     
  593.     def __repr__(self):
  594.         return '<%s for %r>' % (self.__class__.__name__, self.menu)
  595.  
  596.     
  597.     def ParentPopup(self):
  598.         pmenu = self.menu._parentmenu
  599.         return None if pmenu is None else pmenu.popup.vlist
  600.  
  601.     ParentPopup = property(ParentPopup)
  602.     
  603.     def CalcSize(self):
  604.         self.SetItemCount(len(self.menu))
  605.         height = 0
  606.         dc = wx.MemoryDC()
  607.         dc.Font = self.font
  608.         s = self.skin
  609.         padx = self.padding[0]
  610.         iconsize = s.iconsize
  611.         subw = s.submenuicon.Width
  612.         sepheight = s.separatorimage.Size.height
  613.         itemheight = self.itemheight
  614.         textExtent = dc.GetTextExtent
  615.         labelw = accelw = 0
  616.         for item in self.menu:
  617.             if item.Kind == ITEM_SEPARATOR:
  618.                 height += sepheight
  619.                 continue
  620.             height += itemheight
  621.             labelw = max(labelw, textExtent(item.Label)[0])
  622.             accelw = max(accelw, textExtent(item.AccelText)[0])
  623.         
  624.         self.accelColumnX = padx + iconsize + padx + padx + labelw + padx
  625.         width = self.accelColumnX + padx + max(accelw, subw) + padx
  626.         self.MinSize = self.Size = wx.Size(width, height)
  627.  
  628.     
  629.     def OnDrawItem(self, dc, rect, n):
  630.         item = self.menu[n]
  631.         kind = item.Kind
  632.         s = self.skin
  633.         iconsize = s.iconsize
  634.         submenuicon = s.submenuicon
  635.         padx = self.padding.x
  636.         selected = self.IsSelected(n)
  637.         drawbitmap = dc.DrawBitmap
  638.         drawlabel = dc.DrawLabel
  639.         if kind == ITEM_SEPARATOR:
  640.             s.separatorimage.Draw(dc, rect, n)
  641.         else:
  642.             dc.Font = self.font
  643.             if not item.IsEnabled():
  644.                 fg = 'disabled'
  645.             elif selected:
  646.                 fg = 'selection'
  647.             else:
  648.                 fg = 'normal'
  649.             dc.TextForeground = getattr(s.fontcolors, fg)
  650.             grect = Rect(*rect)
  651.             grect.width = padx + iconsize + padx
  652.             bmap = item.Bitmap
  653.             if bmap and bmap.Ok():
  654.                 bmap = bmap.ResizedSmaller(iconsize)
  655.                 drawbitmap(bmap, grect.HCenter(bmap), rect.VCenter(bmap), True)
  656.             
  657.             if item.IsCheckable() and item.IsChecked():
  658.                 if bmap:
  659.                     checkx = grect.Right - s.checkedicon.Width
  660.                 else:
  661.                     checkx = grect.HCenter(s.checkedicon)
  662.                 if kind == ITEM_CHECK:
  663.                     drawbitmap(s.checkedicon, checkx, rect.VCenter(s.checkedicon), True)
  664.                 elif kind == ITEM_RADIO:
  665.                     drawbitmap(s.checkedicon, checkx, rect.VCenter(s.checkedicon), True)
  666.                 
  667.             
  668.             rect.Subtract(left = iconsize + 3 * padx)
  669.             drawlabel(item.Label, rect, indexAccel = item.Text.split('\t')[0].find('&'), alignment = ALIGN_CENTER_VERTICAL)
  670.             rect.Subtract(right = submenuicon.Width + padx)
  671.             if item.SubMenu is not None:
  672.                 drawbitmap(submenuicon, rect.Right, rect.VCenter(submenuicon), True)
  673.             
  674.             acceltext = item.AccelText
  675.             if acceltext:
  676.                 rect.x = self.accelColumnX + padx
  677.                 drawlabel(acceltext, rect, alignment = ALIGN_CENTER_VERTICAL)
  678.             
  679.  
  680.     
  681.     def OnDrawBackground(self, dc, rect, n):
  682.         s = self.skin
  683.         bgs = s.backgrounds
  684.         bgname = None if self.menu[n].Kind != ITEM_SEPARATOR and self.IsSelected(n) else 'item'
  685.         bg = getattr(bgs, bgname, None)
  686.         if bg:
  687.             bg.Draw(dc, rect, n)
  688.         
  689.  
  690.     
  691.     def PaintMoreBackground(self, dc, rect):
  692.         g = self.skin.backgrounds.gutter
  693.         if g:
  694.             g.Draw(dc, Rect(rect.x, rect.y, self.skin.iconsize + self.padding.x * 2, rect.height))
  695.         
  696.  
  697.     
  698.     def OnMeasureItem(self, n):
  699.         item = self.menu[n]
  700.         kind = item.Kind
  701.         if kind == ITEM_SEPARATOR:
  702.             return self.sepheight
  703.         else:
  704.             return self.itemheight
  705.  
  706.     
  707.     def OnPopup(self):
  708.         if self.menu._windowless and self.TopMenu == self.menu:
  709.             if not hasattr(self, 'traytimer'):
  710.                 self.traytimer = UMenuTrayTimer(self)
  711.             
  712.             self.traytimer.Start(50)
  713.         
  714.         if not self.menu._parentmenu:
  715.             self._grabkeyboard()
  716.         
  717.         if wx.LeftDown():
  718.             self._leftbuttondown = True
  719.         
  720.         if not self.HasCapture():
  721.             self.CaptureMouse()
  722.         
  723.         self.SetCursor(StockCursor(CURSOR_DEFAULT))
  724.         self.SetFocus()
  725.  
  726.     
  727.     def Dismiss(self):
  728.         if hasattr(self, 'traytimer'):
  729.             self.traytimer.Stop()
  730.         
  731.         if self.menu._childmenu:
  732.             self.menu._childmenu.Dismiss()
  733.             self.menu._childmenu = None
  734.         
  735.         while self.HasCapture():
  736.             self.ReleaseMouse()
  737.         self.Parent.Hide()
  738.         m = self.menu
  739.         if m._parentmenu is None:
  740.             if hasattr(self, 'focusHandler'):
  741.                 self.focusHandler.close()
  742.                 del self.focusHandler
  743.             
  744.             wx.CallAfter(self.menu.OnDismiss)
  745.         else:
  746.             m._parentmenu._childmenu = None
  747.  
  748.     
  749.     def DismissRoot(self):
  750.         self.TopMenu.Dismiss()
  751.  
  752.     
  753.     def TopMenu(self):
  754.         m = self.menu
  755.         while m._parentmenu is not None:
  756.             m = m._parentmenu
  757.         return m
  758.  
  759.     TopMenu = property(TopMenu)
  760.     
  761.     def UpdateSkin(self):
  762.         self.SetMargins(wx.Point(0, 0))
  763.         s = self.skin = self.menu.skin
  764.         self.sepheight = s.separatorimage.Size.height
  765.         
  766.         try:
  767.             self.font = s.font
  768.         except KeyError:
  769.             self.font = default_font()
  770.  
  771.         self.fontheight = s.font.Height
  772.         
  773.         try:
  774.             self.padding = s.padding
  775.         except Exception:
  776.             self.padding = wx.Point(3, 3)
  777.  
  778.         self.itemheight = int(self.fontheight + self.padding.y * 2)
  779.         self.Background = s.backgrounds.menu
  780.  
  781.     
  782.     def Window(self):
  783.         return self.menu.Window
  784.  
  785.     Window = property(Window)
  786.     
  787.     def _grabkeyboard(self):
  788.         if 'wxMSW' in wx.PlatformInfo:
  789.             self._focusWin = wx.Window.FindFocus()
  790.         elif 'wxGTK' in wx.Platform:
  791.             self._focusWin = self
  792.         else:
  793.             self._focusWin = None
  794.         f = self._focusWin
  795.         if f:
  796.             self.focusHandler = FocusHandler(self, f)
  797.         
  798.  
  799.     
  800.     def _showsubmenu(self, i, highlight = False):
  801.         item = self.menu[i]
  802.         submenu = item.SubMenu
  803.         child = self.menu._childmenu
  804.         if child is submenu:
  805.             return None
  806.         
  807.         if child is not None:
  808.             if child:
  809.                 child.Dismiss()
  810.             
  811.             self.menu._childmenu = None
  812.         
  813.         if i != -1 and submenu is not None:
  814.             r = self.ClientRect
  815.             r.Y = self.GetItemY(i)
  816.             r.Height = self.OnMeasureItem(i)
  817.             self.menu._childmenu = submenu
  818.             submenu._parentmenu = self.menu
  819.             submenu._parentindex = i
  820.             submenu.PopupMenu(r.ToScreen(self), submenu = True)
  821.             if highlight:
  822.                 submenu.popup.vlist.Selection = 0
  823.             
  824.         
  825.  
  826.     
  827.     def _on_submenu_timer(self):
  828.         i = self.Selection
  829.         if i != -1 and self.IsShown() and FindWindowAtPointer() is self:
  830.             self._showsubmenu(i)
  831.         
  832.  
  833.     
  834.     def _listbox(self, e):
  835.         self.timer.Start(self.menuOpenDelayMs, True)
  836.  
  837.     
  838.     def _emit_menuevent(self, id, type = wx.wxEVT_COMMAND_MENU_SELECTED):
  839.         event = wx.CommandEvent(type, id)
  840.         self.menu.Handler.AddPendingEvent(event)
  841.  
  842.     
  843.     def _mouseevents(self, e, wxEVT_MOTION = wxEVT_MOTION, FindWindowAtPoint = FindWindowAtPoint, UberButton = UberButton):
  844.         rect = self.ClientRect
  845.         pt = e.Position
  846.         if not rect.Contains(pt):
  847.             menu = self.ParentPopup
  848.             oldmenu = self
  849.             while menu:
  850.                 pt = menu.ScreenToClient(oldmenu.ClientToScreen(pt))
  851.                 if menu.ClientRect.Contains(pt):
  852.                     (e.m_x, e.m_y) = pt
  853.                     return menu._mouseevents(e)
  854.                 
  855.                 oldmenu = menu
  856.                 menu = menu.ParentPopup
  857.             
  858.             try:
  859.                 button = self.TopMenu._button
  860.             except AttributeError:
  861.                 pass
  862.  
  863.             if e.GetEventType() == wxEVT_MOTION:
  864.                 ctrl = FindWindowAtPoint(self.ClientToScreen(e.Position))
  865.                 if ctrl is not None:
  866.                     if getattr(self, '_motionswitch', -1) is ctrl:
  867.                         self._motionswitch = None
  868.                         self.DismissRoot()
  869.                         ctrl.menu._showquick = True
  870.                         ctrl.OnLeftDown()
  871.                     elif isinstance(ctrl, UberButton) and ctrl.menubarmode and hasattr(ctrl, 'menu'):
  872.                         if ctrl.Parent is button.Parent and ctrl is not button:
  873.                             self._motionswitch = ctrl
  874.                         
  875.                     
  876.                 
  877.             
  878.         
  879.         
  880.         try:
  881.             cb = self.mouseCallbacks[e.EventType]
  882.         except KeyError:
  883.             pass
  884.  
  885.         cb(e)
  886.  
  887.     
  888.     def _motion(self, e):
  889.         p = e.Position
  890.         i = None if self.ClientRect.Contains(p) else -1
  891.         s = self.Selection
  892.         if i != s:
  893.             p = self.ParentPopup
  894.             if p is not None:
  895.                 pi = getattr(self.menu, '_parentindex', None)
  896.                 if pi is not None and p.Selection != pi:
  897.                     p.Selection = pi
  898.                 
  899.             
  900.             self.SetSelection(i)
  901.             self._emit_lbox_selection(i)
  902.         
  903.  
  904.     
  905.     def LeafMenu(self):
  906.         s = self.menu
  907.         while s._childmenu:
  908.             s = s._childmenu
  909.         return s
  910.  
  911.     LeafMenu = property(LeafMenu)
  912.     
  913.     def _keydown(self, e):
  914.         self = self.LeafMenu.popup.vlist
  915.         code = e.KeyCode
  916.         i = self.Selection
  917.         j = -1
  918.         m = self.menu
  919.         if code == wx.WXK_DOWN:
  920.             j = (i + 1) % len(m)
  921.             while j != i and m[j].Kind == wx.ITEM_SEPARATOR:
  922.                 j = (j + 1) % len(m)
  923.         elif code == wx.WXK_UP:
  924.             if i == -1:
  925.                 i = len(m)
  926.             
  927.             j = (i - 1) % len(m)
  928.             while j != i and m[j].Kind == wx.ITEM_SEPARATOR:
  929.                 j = (j - 1) % len(m)
  930.         elif code == wx.WXK_RETURN:
  931.             return self._activate_item(i, submenu = True, highlight = True)
  932.         elif code == wx.WXK_RIGHT:
  933.             if i == -1:
  934.                 pass
  935.             elif m[i].SubMenu is not None:
  936.                 self.timer.Stop()
  937.                 return self._showsubmenu(i, highlight = True)
  938.             
  939.             while m._parentmenu:
  940.                 m = m._parentmenu
  941.             next = getattr(m, '_next', None)
  942.             if next is not None:
  943.                 wx.CallAfter(self.DismissRoot)
  944.                 next._showquick = True
  945.                 wx.CallAfter(next._button.OnLeftDown)
  946.             
  947.         elif code == wx.WXK_ESCAPE:
  948.             self.Dismiss()
  949.         elif code == wx.WXK_LEFT:
  950.             if m._parentmenu:
  951.                 self.Dismiss()
  952.             else:
  953.                 prev = getattr(self.menu, '_prev', None)
  954.                 if prev is not None:
  955.                     wx.CallAfter(self.DismissRoot)
  956.                     prev._showquick = True
  957.                     wx.CallAfter(prev._button.OnLeftDown)
  958.                 
  959.         elif code < 256:
  960.             self._on_char(unichr(e.UnicodeKey))
  961.         
  962.         if j != -1:
  963.             self.SetSelection(j)
  964.         
  965.  
  966.     
  967.     def _on_char(self, char):
  968.         char = char.lower()
  969.         items = []
  970.         for item in self.menu:
  971.             amp_char = GetAmpChar(item)
  972.             if amp_char is not None and char == amp_char.lower():
  973.                 return self._activate_item(item, submenu = True, highlight = True)
  974.                 continue
  975.         
  976.         items = []
  977.         for i in rotated(range(0, len(self.menu)), -(self.Selection) - 1):
  978.             item = self.menu[i]
  979.             label_text = item.GetItemLabelText()
  980.             if label_text and label_text[0].lower() == char:
  981.                 items.append(i)
  982.                 continue
  983.         
  984.         if len(items) == 1:
  985.             self._activate_item(items[0], submenu = True, highlight = True)
  986.         elif len(items) > 1:
  987.             self.SetSelection(items[0])
  988.         
  989.  
  990.     
  991.     def _rdown(self, e):
  992.         p = e.Position
  993.         rect = self.ClientRect
  994.         if not rect.Contains(p):
  995.             return self.DismissRoot()
  996.         
  997.  
  998.     
  999.     def _ldown(self, e):
  1000.         p = e.Position
  1001.         rect = self.ClientRect
  1002.         if not rect.Contains(p):
  1003.             return self.DismissRoot()
  1004.         
  1005.         i = self.HitTest(p)
  1006.         if i != -1:
  1007.             if self.menu[i].SubMenu is not None:
  1008.                 self.timer.Stop()
  1009.                 return self._showsubmenu(i)
  1010.             
  1011.         
  1012.  
  1013.     
  1014.     def _lup(self, e):
  1015.         p = e.Position
  1016.         i = self.HitTest(e.Position)
  1017.         if self.ClientRect.Contains(p):
  1018.             self._activate_item(i)
  1019.         else:
  1020.             ctrl = FindWindowAtPointer()
  1021.             if not isinstance(ctrl, UberButton) or not (ctrl.type == 'menu'):
  1022.                 self.DismissRoot()
  1023.             
  1024.  
  1025.     
  1026.     def _activate_item(self, i, submenu = False, highlight = False):
  1027.         if isinstance(i, int):
  1028.             if i == -1:
  1029.                 return None
  1030.             
  1031.             item = self.menu[i]
  1032.         else:
  1033.             item = i
  1034.             i = self.menu.IndexOf(item)
  1035.         if submenu:
  1036.             if item.SubMenu is not None:
  1037.                 self.timer.Stop()
  1038.                 if not self.Selection == i:
  1039.                     self.SetSelection(i)
  1040.                 
  1041.                 return self._showsubmenu(i, highlight = highlight)
  1042.             
  1043.         
  1044.         if item.Kind != ITEM_SEPARATOR and item.IsEnabled() and item.SubMenu is None:
  1045.             if item.IsCheckable():
  1046.                 item.Check(not item.IsChecked())
  1047.             
  1048.             self._emit_menuevent(item.Id)
  1049.             self.DismissRoot()
  1050.         
  1051.  
  1052.     
  1053.     def _capturechanged(self, e):
  1054.         
  1055.         def active():
  1056.             
  1057.             try:
  1058.                 if self.menu._windowless or not hasattr(self.menu.Window.Top, 'IsActive'):
  1059.                     return True
  1060.                 else:
  1061.                     return self.menu.Window.Top.IsActive()
  1062.             except Exception:
  1063.                 print_exc()
  1064.                 return True
  1065.  
  1066.  
  1067.         ((None,), wx.CallAfter)((lambda : None if not active() else None))
  1068.  
  1069.  
  1070.  
  1071. class MenuWindowBase(object):
  1072.     
  1073.     def __init__(self, parent, menu):
  1074.         self.vlist = MenuListBox(self, menu)
  1075.         self.UpdateSkin()
  1076.         self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
  1077.         self.Bind(wx.EVT_PAINT, self._paint)
  1078.  
  1079.     
  1080.     def reassign(self, parent, menu):
  1081.         self.Reparent(parent)
  1082.         self.vlist.reassign(menu)
  1083.  
  1084.     
  1085.     def _paint(self, e):
  1086.         dc = wx.BufferedPaintDC(self)
  1087.         self.bg.Draw(dc, self.ClientRect)
  1088.  
  1089.     
  1090.     def UpdateSkin(self):
  1091.         s = self.skin = self.vlist.menu.skin
  1092.         if self.Sizer and not wx.IsDestroyed(self.Sizer):
  1093.             self.Sizer.Clear()
  1094.         
  1095.         self.framesize = s.get('framesize', skin.ZeroMargins)
  1096.         self.Sizer = self.framesize.Sizer(self.vlist)
  1097.         self.bg = s.frame
  1098.  
  1099.     
  1100.     def PopupMenu(self, pos = None, submenu = False):
  1101.         v = self.vlist
  1102.         v.menu.Handler._menu_open(menu = v.menu)
  1103.         v.SetSelection(-1)
  1104.         v.CalcSize()
  1105.         self.Fit()
  1106.         self.Sizer.Layout()
  1107.         if isinstance(self.bg, SplitImage4):
  1108.             self.Cut(self.bg.GetBitmap(self.Size))
  1109.         else:
  1110.             self.Cut()
  1111.         pos = None if pos is None else pos
  1112.         
  1113.         try:
  1114.             
  1115.             try:
  1116.                 disp = Monitor.GetFromPoint(pos[:2]).Geometry
  1117.             except Exception:
  1118.                 disp = Monitor.GetFromPoint(pos.BottomRight).Geometry
  1119.  
  1120.         except Exception:
  1121.             print_exc()
  1122.             log.critical('could not find display for %s, falling back to zero', pos)
  1123.             disp = Monitor.All()[0].Geometry
  1124.  
  1125.         size = self.Size
  1126.         rects = []
  1127.         
  1128.         add = lambda *seq: []([ RectPS(p, size) for p in seq ])
  1129.         singlepoint = len(pos) == 2
  1130.         offset = (None, None) if singlepoint else 0
  1131.         if singlepoint:
  1132.             pos = wx.RectPS(pos, (0, 0))
  1133.         
  1134.         w = Point(size.width - offset, 0)
  1135.         h = Point(0, size.height - offset)
  1136.         wh = Point(size.width - offset, size.height - offset)
  1137.         difftop = Point(0, self.framesize.top)
  1138.         diffbottom = Point(0, self.framesize.bottom)
  1139.         if submenu:
  1140.             add(pos.TopRight - difftop, pos.TopLeft - w - difftop, (pos.BottomRight - h) + diffbottom, (pos.BottomLeft - wh) + diffbottom)
  1141.         else:
  1142.             add(pos.BottomLeft, pos.TopLeft - h, pos.BottomRight - w, pos.TopRight - h, pos.TopRight - wh, pos.BottomLeft - h)
  1143.         for rect in rects:
  1144.             if disp.ContainsRect(rect):
  1145.                 self._showat(rect)
  1146.                 return None
  1147.                 continue
  1148.         
  1149.         rect = rects[0]
  1150.         if hasattr(v.menu, '_button'):
  1151.             brect = v.menu._button.ScreenRect
  1152.             if rect.Intersects(brect):
  1153.                 rect.Offset((brect.Width, 0))
  1154.             
  1155.         
  1156.         self._showat(rect)
  1157.  
  1158.     
  1159.     def _showat(self, rect, nofade = False):
  1160.         self.SetRect(rect)
  1161.         self.EnsureInScreen()
  1162.         if nofade:
  1163.             self.Show()
  1164.         elif getattr(self.vlist.menu, '_showquick', False):
  1165.             self.vlist.menu._showquick = False
  1166.             self.Show()
  1167.         else:
  1168.             fadein(self, 'xfast')
  1169.         if wxMSW:
  1170.             CallLater((1,), (lambda : None if self else None))
  1171.         
  1172.         self.vlist.OnPopup()
  1173.  
  1174.  
  1175.  
  1176. class MenuPopupWindow(MenuWindowBase, wx.PopupWindow):
  1177.     
  1178.     def __init__(self, parent, menu):
  1179.         wx.PopupWindow.__init__(self, parent)
  1180.         MenuWindowBase.__init__(self, parent, menu)
  1181.  
  1182.  
  1183.  
  1184. class MenuFrameWindow(MenuWindowBase, wx.Frame):
  1185.     
  1186.     def __init__(self, parent, menu):
  1187.         wx.Frame.__init__(self, parent)
  1188.         MenuWindowBase.__init__(self, parent, menu)
  1189.  
  1190.  
  1191. from weakref import WeakValueDictionary
  1192.  
  1193. class MenuEventHandler(wx.EvtHandler):
  1194.     
  1195.     def __init__(self, parentFrame):
  1196.         wx.EvtHandler.__init__(self)
  1197.         parentFrame.PushEventHandler(self)
  1198.         self.Bind(wx.EVT_MENU, self._MenuEventHandler__menu)
  1199.         if 'wxMac' not in wx.PlatformInfo:
  1200.             self.Bind(wx.EVT_MENU_OPEN, self._menu_open)
  1201.         
  1202.         if 'wxMSW' in wx.PlatformInfo:
  1203.             parentFrame.BindWin32(WM_INITMENUPOPUP, self._initmenupopup)
  1204.         
  1205.         self.cbs = WeakValueDictionary()
  1206.         self.showcbs = { }
  1207.         if wxMSW:
  1208.             self.hwndMap = WeakValueDictionary()
  1209.         
  1210.  
  1211.     
  1212.     def AddCallback(self, id, callback):
  1213.         self.cbs[id] = callback
  1214.  
  1215.     
  1216.     def AddShowCallback(self, id, callback):
  1217.         self.showcbs[id] = callback
  1218.  
  1219.     
  1220.     def __menu(self, e):
  1221.         id = e.Id
  1222.         
  1223.         try:
  1224.             cb = self.cbs[id]
  1225.         except KeyError:
  1226.             e.Skip()
  1227.  
  1228.         cb()
  1229.  
  1230.     
  1231.     def _menu_open(self, e = None, menu = None):
  1232.         if e is not None:
  1233.             e.Skip()
  1234.             menu = e.Menu
  1235.         
  1236.         if menu is None:
  1237.             return None
  1238.         
  1239.         
  1240.         try:
  1241.             cb = self.showcbs[menu.Id]
  1242.         except KeyError:
  1243.             pass
  1244.  
  1245.         cb()
  1246.  
  1247.     if wxMSW:
  1248.         
  1249.         def _initmenupopup(self, hWnd, msg, wParam, lParam):
  1250.             
  1251.             try:
  1252.                 menu = self.hwndMap[wParam]
  1253.             except KeyError:
  1254.                 return None
  1255.  
  1256.             if menu._parentmenu:
  1257.                 evt = PyCommandEvent(wxEVT_MENU_OPEN, menu.Id)
  1258.                 evt.Menu = menu
  1259.                 menu.Handler.ProcessEvent(evt)
  1260.             
  1261.  
  1262.     
  1263.  
  1264.  
  1265. class FocusHandler(wx.EvtHandler):
  1266.     
  1267.     def __init__(self, menu, ctrl):
  1268.         wx.EvtHandler.__init__(self)
  1269.         self._menu = menu
  1270.         self._ctrl = ctrl
  1271.         self.Bind(wx.EVT_KEY_DOWN, self._menu._keydown)
  1272.         self.Bind(wx.EVT_NAVIGATION_KEY, self._menu._keydown)
  1273.         self.wantschars = bool(ctrl.WindowStyleFlag & wx.WANTS_CHARS)
  1274.         if not self.wantschars:
  1275.             ctrl.SetWindowStyleFlag(ctrl.WindowStyleFlag | wx.WANTS_CHARS)
  1276.         
  1277.         ctrl.PushEventHandler(self)
  1278.  
  1279.     
  1280.     def close(self):
  1281.         ctrl = self._ctrl
  1282.         ctrl.RemoveEventHandler(self)
  1283.         f = ctrl.WindowStyleFlag
  1284.         if not self.wantschars:
  1285.             f = f & ~(wx.WANTS_CHARS)
  1286.             ctrl.SetWindowStyleFlag(f)
  1287.         
  1288.  
  1289.     
  1290.     def SetMenu(self, menu):
  1291.         self._menu = menu
  1292.  
  1293.     
  1294.     def OnKeyDown(self, event):
  1295.         self._menu.OnKeyDown(event)
  1296.  
  1297.  
  1298.  
  1299. def menuEventHandler(f):
  1300.     
  1301.     try:
  1302.         return f._menuevthandler
  1303.     except AttributeError:
  1304.         h = f._menuevthandler = MenuEventHandler(f)
  1305.  
  1306.     return h
  1307.  
  1308. from gui.toolbox.keynames import keynames, modifiernames
  1309.  
  1310. def menuevt(item):
  1311.     return wx.CommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, item.Id)
  1312.  
  1313.  
  1314. def GetAccelText(item):
  1315.     a = item.Accel
  1316.     return None if not a else _getacceltext(a.Flags, a.KeyCode)
  1317.  
  1318. if not hasattr(wx, '_MenuItem'):
  1319.     wx._MenuItem = wx.MenuItem
  1320.  
  1321. wx._MenuItem.AccelText = property(GetAccelText)
  1322.  
  1323. def GetAmpChar(item):
  1324.     text = item.Text
  1325.     amp_index = text.find('&')
  1326.     if amp_index != -1 and amp_index < len(text) - 1:
  1327.         return text[amp_index + 1]
  1328.     
  1329.  
  1330.  
  1331. def _getacceltext(modifiers, key, joinstr = '+'):
  1332.     return [](_[1] + [
  1333.         keynames.get(key, chr(key).upper())])
  1334.  
  1335. _getacceltext = memoize(_getacceltext)
  1336. from collections import deque
  1337.  
  1338. def rotated(iter, n):
  1339.     d = deque(iter)
  1340.     d.rotate(n)
  1341.     return d
  1342.  
  1343.